/**
 * @author shahma
 */
dojo.provide("medryx.orm.tests.EntityManagerTest")
dojo.require("doh.runner");
dojo.require("medryx.orm.EntityManager");
dojo.require("doh.TestCase");
dojo.require("medryx.exceptions.UnmanagedEntityClassException");
dojo.require("medryx.orm.tests.support.MockPersistenceService");
(function(){

    var testEntityClassMapping = medryx.TestEntityClass.mapping;
    var scaffold = {
        setUp: function(){
            this.persistenceService = new MockPersistenceService();
            this.ef = new medryx.orm.EntityManager(this.persistenceService);
            this.ef.registerClass("medryx.TestEntityClass", this.testEntityClassMapping, null, "Test");
			
            this.ef.registerClass("medryx.TestAssociationClass", null, null, "Association");
        },
        testEntityClassMapping: testEntityClassMapping,
        bracketStringConverter: {
            fromStringToProperty: function(strValue){
                return strValue.replace(/[<>]/, "");
            },
            fromPropertyToString: function(propValue){
                return "<" + propValue + ">";
            }
        }
    };
    
    var SyncTest = doh.TestCase;
    var AsyncTest = doh.AsyncTestCase;
   
    doh.register("medryx.orm.tests.EntityManagerTest", [

new SyncTest("testIsPropertyLoaded - all loaded", function() {
	var item = new Association({id:1, value:"TEST", sister: {
		id: 2,
		value: "TEST2",
		sister: 3,
		collection: [1, 2, 3]
	}, collection:[1, 2, 3]});
	assertTrue(ef.isPropertyLoaded(item, "*"));
	assertTrue(ef.isPropertyLoaded(item, "id"));
	assertTrue(ef.isPropertyLoaded(item, "value"));
	assertTrue(ef.isPropertyLoaded(item, "sister"));
	assertTrue(ef.isPropertyLoaded(item, "sister.*"));
	assertTrue(ef.isPropertyLoaded(item, "collection"));
	
}, scaffold),
new SyncTest("testIsPropertyLoaded - not loaded", function() {
    var item = new Association({id:1});
    assertTrue(ef.isPropertyLoaded(item, "id"));
    assertFalse(ef.isPropertyLoaded(item, "value"));
    assertFalse(ef.isPropertyLoaded(item, "sister"));
    assertFalse(ef.isPropertyLoaded(item, "collection"));
}, scaffold),
new SyncTest("testIsPropertyLoaded - dot", function() {
    var item = new Association({id:1, value:"TEST1", sister:{id:2, value:"TEST2", sister:5}, collection:[{
		id: 3,
		value: "TEST3"
	}]});
    assertTrue(ef.isPropertyLoaded(item, "id"));
    assertTrue(ef.isPropertyLoaded(item, "value"));
    assertTrue(ef.isPropertyLoaded(item, "sister"));
	assertTrue(ef.isPropertyLoaded(item, "sister.value"));
	assertFalse(ef.isPropertyLoaded(item, "sister.sister.value"));
	assertFalse(ef.isPropertyLoaded(item, "sister.collection"));
    assertTrue(ef.isPropertyLoaded(item, "collection.value"));
	assertFalse(ef.isPropertyLoaded(item, "collection.sister"));
}, scaffold),


new SyncTest("testInstrumentConstructor", function() {
	var e = new Association();
	
	assertEqual("medryx.TestAssociationClass", e.declaredClass);
	e.setValue("TEST");
	assertEqual("TEST", e.value);
	
	assertTrue(ef.isManagedEntity(e));
	
}, scaffold),

new SyncTest("testInstrumentConstructor-existing entity", function() {
	var existingEntity = ef.buildEntity("Association", 20);
    var e = new Association({id:20});
    assertEqual(20, e.id);
	
    assertTrue(e === existingEntity);
	
	var f = new Association(20);
	assertTrue(f === existingEntity);
   
}, scaffold),

new SyncTest("testConvertClassToKey", function(){
        var key = ef.convertClassToKey("medryx.TestEntityClass");
      //this is a noop
	  assertEqual("medryx.TestEntityClass", key);
    }, scaffold), 
	
	


new SyncTest("testLabels", function() {
		var labels = ef.getLabelProperties("Test");
		assertEqual(2, labels.length);
		assertEqual("defaultLazyProperty", labels[1].propertyName);
	}, scaffold),
	


new SyncTest("testGetMapping", function(){
        var mapping = ef.getMapping("medryx.TestEntityClass");
        assertNotNull(mapping, "mapping should exist for TestEntityClass");
        assertInstanceOf(mapping.id, medryx.orm.Identifier);
        assertInstanceOf(mapping.defaultLazyProperty, medryx.orm.Property);
    }, scaffold), 


new SyncTest("testGetNativeMapping", function(){
        var mapping = ef.getMapping("medryx.TestAssociationClass");
        assertNotNull(mapping, "mapping should exist for TestEntityClass");
        assertInstanceOf(mapping.id, medryx.orm.Identifier);
        assertInstanceOf(mapping.value, medryx.orm.Property);
    }, scaffold), 


new SyncTest("testGetUnmanagedMapping", function(){
        try {
            var entity = ef.getMapping("AsyncTest");
            fail("should throw error for getMapping of unmanaged class");
        } 
        catch (e) {
            assertInstanceOf(e, medryx.exceptions.UnmanagedEntityClassException, "expected UnmanagedEntityClassException for testGetUnmanagedMapping");
        }
        
    }, scaffold), 


new SyncTest("testInstantiateClassByName", function(){
        var entity = ef.instantiateClassByName("medryx.TestEntityClass");
        assertInstanceOf(entity, medryx.TestEntityClass, "entity should be instantiated from string class as TestEntityClass");
    }, scaffold), 


new SyncTest("testRegisterEntity", function(){
        var ef = new medryx.orm.EntityManager(new MockPersistenceService());
        ef.registerClass("medryx.TestEntityClass", testEntityClassMapping);
        assertTrue(ef.isManagedClass("medryx.TestEntityClass"), "medryx.TestEntityClass shoudl now be a managed class");
    }, {
        testEntityClassMapping: testEntityClassMapping
    }), 


new SyncTest("testIsManagedClass", function(){
        var ef = new medryx.orm.EntityManager(new MockPersistenceService());
        assertFalse(ef.isManagedClass("medryx.TestEntityClass"), "medryx.TestEntityClass should not be managed yet");
        ef.registerClass("medryx.TestEntityClass", testEntityClassMapping);
        assertTrue(ef.isManagedClass("medryx.TestEntityClass"), "medryx.TestEntityClass shoudl now be a managed class");
    }, {
        testEntityClassMapping: testEntityClassMapping
    }), 


new SyncTest("testBuildEntity", function(){
        var entity = ef.buildEntity("medryx.TestEntityClass");
        assertDefined(entity, "entity should be defined after call to buildEntity");
        assertInstanceOf(entity, medryx.TestEntityClass, "entity should be instnaceof TestEntityClass");
    }, scaffold), 


new SyncTest("testBuildUnmanagedEntity", function(){
        try {
            var entity = ef.buildEntity("AsyncTest");
            fail("should throw error for bulding entity of unmanaged class");
        } 
        catch (e) {
            assertInstanceOf(e, medryx.exceptions.UnmanagedEntityClassException, "expected UnmanagedEntityClassException ofr testBuildUnmanagedEntity");
        }
    }, scaffold), 


new SyncTest("testInitializeManagedProperties", function(){
        var entity = new medryx.TestEntityClass();
        var mapping = testEntityClassMapping;
        
        assertNull(entity.id, "before initialization, id should be null");
        assertNull(entity.defaultLazyProperty, "before initialization, defaultLazyProperty should be null");
        
        ef.initializeManagedProperties(entity);
        assertEqual("id", ef.getIdentifierProperty(entity));
        assertInstanceOf(entity.id, medryx.orm.Identifier, "after initialization, id should be a medryx.orm.identifier");
        assertInstanceOf(entity.defaultLazyProperty, medryx.orm.Property, "after initialization, defaultLazyProperty should be a medryx.orm.Property");
    }, scaffold), 


new SyncTest("testInitializeManagedPropertiesNative", function(){
        var entity = new medryx.TestAssociationClass();
        
        assertInstanceOf(entity.id, medryx.orm.Identifier, "before initialization, id should be a medryx.orm.identifier");
        assertInstanceOf(entity.value, medryx.orm.Property, "before initialization, defaultLazyProperty should be a medryx.orm.Property");
        
        ef.initializeManagedProperties(entity, "NATIVE"); //should be noop
        assertEqual("id", ef.getIdentifierProperty(entity));
        assertInstanceOf(entity.id, medryx.orm.Identifier, "after initialization, id should be a medryx.orm.identifier");
        assertInstanceOf(entity.value, medryx.orm.Property, "after initialization, value should be a medryx.orm.Property");
    }, scaffold), 


new SyncTest("testPopulateAll", function(){
        var entity = new medryx.TestAssociationClass();
        var bag = {
            id: 1,
            value: 10
        };
        ef.populateInstance(entity, bag);
        assertEqual(1, entity.id, "id should be 1 after populateInstance");
        assertEqual(10, entity.value, "value should be 10 after populateInstance");
        
    }, scaffold), 


new SyncTest("testPopulateNoBagProperties", function(){
        var entity = new medryx.TestAssociationClass();
        var bag = {};
        ef.populateInstance(entity, bag);
        assertInstanceOf(entity.id, medryx.orm.Identifier, "id should still be an Identifier for empty bag");
        assertInstanceOf(entity.value, medryx.orm.Property, "value should still be an Property for empty bag");
        
    }, scaffold), 


new SyncTest("testPopulateNoBag", function(){
        var entity = new medryx.TestAssociationClass();
        ef.populateInstance(entity);
        assertInstanceOf(entity.id, medryx.orm.Identifier, "id should still be an Identifier for no bag");
        assertInstanceOf(entity.value, medryx.orm.Property, "value should still be an Property for no bag");
        
    }, scaffold), 


new SyncTest("testPopulateId", function(){
        var entity = new medryx.TestAssociationClass();
        ef.initializeManagedProperties(entity);
        ef.populateInstance(entity, 19);
        assertEqual(19, entity.id);
        
    }, scaffold), 


new SyncTest("testPopulateBadBag", function(){
        var entity = new medryx.TestAssociationClass();
        try {
            ef.populateInstance(entity, "sdfklasjdkl;fj");
            fail("should have had an exception for bad bag");
        } 
        catch (e) {
            assertInstanceOf(e, medryx.exceptions.IllegalArgumentException);
        }
        
    }, scaffold), 


new SyncTest("testPopulateNullInitializer", function(){
        var entity = new medryx.TestAssociationClass();
        var bag = {
            value: null
        };
        ef.populateInstance(entity, bag);
        assertNull(entity.value, "value should be null after populateInstance with nullInitializer")
        
    }, scaffold), 


new SyncTest("testPopulateDeep", function(){
        var entity = new medryx.TestEntityClass();
        var bag = {
            id: 1,
            lazyAssociation: {
                id: 1,
                value: "testValue",
                sister: {
                    id: 2,
                    value: "testValue2",
                    sister: 99
                }
            }
        };
        persistenceService.loadResults = {
            value: "THIS IS A TEST"
        };
        ef.populateInstance(entity, bag);
        
        assertInstanceOf(entity.lazyAssociation, "medryx.TestAssociationClass");
        assertEqual("testValue", entity.lazyAssociation.value);
        assertInstanceOf(entity.lazyAssociation.sister, "medryx.TestAssociationClass");
        assertEqual("testValue2", entity.lazyAssociation.sister.value);
        
        
    }, scaffold), 
	
	


new SyncTest("testGetInitializedId", function() {
		var entity = ef.buildEntity("Test");
		var id = ef.getIdentifier(entity);
		console.debug("initialized id: ", id);
		assertTrue(id < 0);
		assertEqual(id, ef.getIdentifier(entity));
		
		var e2 = ef.buildEntity("Test");
		var id2 = ef.getIdentifier(e2);
		assertFalse(id2 == id);
		
	}, scaffold),
	

new AsyncTest("testInstrumentAccessor", function(){
        var entity = new medryx.TestAssociationClass();
        entity.entityManager = ef;
        var map = new medryx.orm.Identifier();
        map.propertyName = "id";
        ef.instrumentAccessor(entity, map);
        assertDefined(entity.getId, "getId should be method after accessor is instrumented");
        assertDefined(entity.getIdAsync, "getIdAsync should be method after accessor is instrumented");
        
        entity.id = 10;
        assertEqual(10, entity.getId(), "sync getId");
        return entity.getIdAsync();
    }, function(id){
        assertEqual(10, id, "async getId");
    }, scaffold), 
	
new AsyncTest("testAyncLazyAccessor", function(){
        var entity = ef.buildEntity("medryx.TestAssociationClass", 10);
        assertEqual(10, entity.getId());
        persistenceService.loadResults = {
            value: "THIS IS A TEST testAyncLazyAccessor"
        };
        return entity.getValueAsync();
    }, function(value){
        assertEqual("THIS IS A TEST testAyncLazyAccessor", value);
    }, scaffold), 
	
	
new SyncTest("testSyncLazyAccessor", function(){
        var entity = ef.buildEntity("medryx.TestAssociationClass", 10);
        assertEqual(10, entity.getId());
        persistenceService.loadResults = {
            value: "THIS IS A TEST testSyncLazyAccessor"
        };
        
        assertEqual("THIS IS A TEST testSyncLazyAccessor", entity.getValue());
    }, scaffold), 
	
	
new SyncTest("testInstrumentActiveRecord", function() {
	   var e = ef.buildEntity("Test");
	   assertDefined(e.loadDeferred);
	   assertDefined(e.save);
	   var saveEntityCalled = false;
	   ef.saveEntity = function(entity) {
	   	   saveEntityCalled = true;
		   assertEqual(e, entity);
	   }
	   e.save();
	   assertTrue(saveEntityCalled);
	   
	    var loadDeferredCalled = false;
       ef.loadDeferred = function(entity) {
           loadDeferredCalled = true;
           assertEqual(e, entity);
       }
       e.loadDeferred();
       assertTrue(loadDeferredCalled);
}, scaffold),

new SyncTest("testGetter", function() {
	  var e = ef.buildEntity("Test");
	  persistenceService.loadResults = {
	  	defaultLazyProperty:"TEST"
	  };
	  assertEqual("TEST", e.getDefaultLazyProperty());
	  assertEqual("TEST", e.getDefaultLazyProperty(), "second time should still work (now optimized)"); 
	  
	  
}, scaffold),

new AsyncTest("testSaveEntity", function() {
	var e = ef.buildEntity("Test");
	e.setDefaultLazyProperty("123");
	persistenceService.saveResults = 123;
	assertTrue(e.isDirty());
	this.entity = e;
	return ef.saveEntity(e);
	
	
}, function(result) {
	assertEqual(123, result);
	assertFalse(entity.isDirty());
	assertEqual("123", persistenceService.lastSaved.properties.defaultLazyProperty);
}, scaffold),

new AsyncTest("testDeleteEntity", function() {
    var e = ef.buildEntity("Test", 11);
    e.setDefaultLazyProperty("123");
    persistenceService.deleteResults = true;
    this.entity = e;
    return ef.deleteEntity(e);
}, function(result) {
    assertTrue(result);
	assertEqual("medryx.TestEntityClass", persistenceService.lastDeleted.className);
	assertEqual(11, persistenceService.lastDeleted.id);
    assertFalse(ef.isManagedEntity(entity));
}, scaffold),

new SyncTest("testInitializeLoadState", function() {
	   var e = ef.buildEntity("Association", {id:1, value:"TEST", sister:2, collection:[2, 3]});
	   assertTrue(ef.isCompletelyLoaded(e), "e was completely initialized in build, so it isCompletelyLoaded");
	   
	    var e2 = ef.buildEntity("Association", {id:3, value:"TEST"});
       assertFalse(ef.isCompletelyLoaded(e2), "e2 was NOT completely initialized in build, so it is NOT completelyLoaded");
	   
	    var e3 = ef.buildEntity("Association", {id:1, value:"TEST"}); //same instance, so these values don't clobber those set above
       assertTrue(ef.isCompletelyLoaded(e3), "e3 was completely initialized in  first build, and the second should be the same instance, so it should be completelyLoaded");
}, scaffold),

new SyncTest("testSetter", function() {
	var e = ef.buildEntity("Test");
	var onDirtyCalled = false;
	
	
	e.setDefaultLazyProperty("TEST-VALUE");
	assertEqual("TEST-VALUE", e.defaultLazyProperty);
	e.onDirty = function(prop, old, newValue) {
        onDirtyCalled = true;
        assertEqual("defaultLazyProperty", prop);
        assertEqual("TEST-VALUE", old);
        assertEqual("TEST-VALUE2", newValue);
    }
	
	
	e.setDefaultLazyProperty(["TEST-VALUE2"]);
	assertEqual("TEST-VALUE2", e.defaultLazyProperty, "should be the same after set with array since field doesn't accept arrays");
	
	e.onDirty = function() {
        //disconnect
    }
	
	var pm = ef.getPropertyManager(e, "defaultLazyProperty");
	pm.array = true;
	
	e.setDefaultLazyProperty(["TEST-VALUE4"]);
    assertEqual(["TEST-VALUE4"], e.defaultLazyProperty, "should use the array value when isArray");
	
	e.setDefaultLazyProperty("TEST-VALUE3");
    assertEqual(["TEST-VALUE3"], e.defaultLazyProperty, "should wrap scalar in array when isArray");
	
	assertTrue(e.isDirty());
	assertTrue(onDirtyCalled);
	assertTrue(dojo.some(e._dirtyProperties, function(prop) { return prop == "defaultLazyProperty"}));
}, scaffold),

new SyncTest("testGetClassName", function(){
        assertEqual("medryx.TestAssociationClass", ef.getClassName("medryx.TestAssociationClass"));
        assertEqual("medryx.TestAssociationClass", ef.getClassName(medryx.TestAssociationClass));
        assertEqual("medryx.TestAssociationClass", ef.getClassName(new medryx.TestAssociationClass()));
    }, scaffold), 
	

new SyncTest("testGetEagerProperties", function(){
        var entity = ef.buildEntity("medryx.TestEntityClass");
        var eagerProperties = ef.getEagerProperties(entity, 99);
        assertEqual(3, eagerProperties.length);
        assertEqual("eagerProperty, eagerCollection, eagerAssociation", eagerProperties.join(", "));
    }, scaffold), 
	
new SyncTest("testInitializeEagerFetchProperties", function(){
        persistenceService.loadResults = {
            id: 99,
            eagerAssociation: 55,
            eagerProperty: "TEST",
            eagerCollection: [1, 2, 3]
        };
        var entity = ef.buildEntity("medryx.TestEntityClass", 88);
        assertEqual(55, entity.eagerAssociation.id);
        assertEqual("TEST", entity.eagerProperty);
        assertEqual(3, entity.eagerCollection.length);
    }, scaffold), 





new AsyncTest("testFetchAsyncFromEntity", function(){
        persistenceService.loadResults = {
            id: 99,
            lazyAssociation: 55,
            defaultLazyProperty: "TEST",
            lazyCollection: [1, 2, 3]
        };
        var entity = ef.buildEntity("medryx.TestEntityClass", 88);
        return entity.loadDeferred(["lazyProperty"]);
    }, function(instance){
        assertEqual("TEST", instance.defaultLazyProperty);
    }, scaffold), 


new SyncTest("testGetValue-EmptyValue", function() {
    var entity = this.entity = ef.buildEntity("Association", {id:100});
    try {
		ef.get(entity, ef.getPropertyManager(entity, "value"), false);
		fail("exception should be thrown because the server does not return value as it is supposed to");
	} catch (e) {
		assertInstanceOf(e, "medryx.exceptions.InvalidServerData");
	}
    
}, scaffold),

new SyncTest("testGetManagedEntity", function(){
        var entity = ef.buildEntity("medryx.TestEntityClass", 88);
        assertEqual(entity, ef.getManagedEntity("medryx.TestEntityClass", 88));
        
    }, scaffold), 


new SyncTest("testOneManagedEntityInstance", function(){
        var entity = ef.buildEntity("medryx.TestEntityClass", 88);
        assertEqual(entity, ef.getManagedEntity("medryx.TestEntityClass", 88));
        var entity2 = ef.buildEntity("medryx.TestEntityClass", 88);
        assertTrue(entity === entity2);
    }, scaffold), 


new SyncTest("testGetManagedEntityBag", function(){
        var entity = ef.buildEntity("medryx.TestEntityClass", {
            id: 88
        });
        assertEqual(entity, ef.getManagedEntity("medryx.TestEntityClass", 88));
        
    }, scaffold), 


new SyncTest("testOneManagedEntityInstanceBag", function(){
        var entity = ef.buildEntity("medryx.TestEntityClass", {
            id: 88
        });
        assertEqual(entity, ef.getManagedEntity("medryx.TestEntityClass", 88));
        var entity2 = ef.buildEntity("medryx.TestEntityClass", {
            id: 88
        });
        assertTrue(entity === entity2);
    }, scaffold),
	
	
	


new SyncTest("aliass", function() {
		 var mapping = ef.getMapping("Test");
        assertNotNull(mapping, "mapping should exist for Test alias");
	},scaffold),
 
new AsyncTest("transient object to saved object", function() {
    var entity = ef.buildEntity("Association");
    assertTrue(ef.isManagedEntity(entity), "entity is managed immediately after building");
	console.debug("entity after build:", entity);	
	persistenceService.saveResults = 99;
	this.entity = entity;
	return ef.saveEntity(entity);
	
}, function() {
	console.debug("entity after save:", entity);  
    assertTrue(ef.isManagedEntity(entity), "entity is still managed immediately after saving");    
    assertEqual(99, entity.id);
    var e2 = ef.buildEntity("Association", {id:99});
    assertTrue(e2 === entity, "e2 and entity should be the same instance");
}, scaffold)


    ]);
})();

